TITLE DSKETTE DATE 01-12-84 DISKETTE BIOS
.XLIST
INCLUDE DSEG.SRC
INCLUDE POSTEQU.SRC
.LIST
INCLUDE SEGMENT.SRC

PUBLIC	DISK_INT_1
PUBLIC	SEEK
PUBLIC	DSKETTE_SETUP
EXTRN	DDS:NEAR

;-- INT 13 --------------------------------------------------------------------
; DISKETTE I/O
;	THIS INTERFACE PROVIDES ACCESS TO THE 5 1/4" DISKETTE DRIVES
;	320/360K DISKETTE DRIVES AND 1.2M DISKETTE DRIVES SUPPORTED
; INPUT
;	(AH)=0	RESET DISKETTE SYSTEM
;		HARD RESET TO NEC, PREPARE COMMAND, RECAL REQD ON ALL DRIVES
;	(AH)=1	READ THE STATUS OF THE SYSTEM INTO (AH)
;		DISKETTE_STATUS FROM LAST OP'N IS USED
;	REGISTERS FOR READ/WRITE/VERIFY/FORMAT
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;	(DH) - HEAD NUMBER (0-1 ALLOWED, NOT VALUE CHECKED)
;	(CH) - TRACK NUMBER (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 TRACK NUMBER
;		 320/360   320/360	     0-39
;		 320/360    1.2M	     0-39
;		  1.2M	    1.2M	     0-79
;	(CL) - SECTOR NUMBER (NOT VALUE CHECKED, NOT USED FOR FORMAT)
;		  MEDIA     DRIVE	 SECTOR NUMBER
;		 320/360   320/360	      1-8/9
;		 320/360    1.2M	      1-8/9
;		  1.2M	    1.2M	      1-15
;	(AL) - NUMBER OF SECTORS (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 MAX NUMBER OF SECTORS
;		 320/360   320/360		 8/9
;		 320/360    1.2M		 8/9
;		  1.2M	    1.2M		 15
;
;	(ES:BX) - ADDRESS OF BUFFER ( REQUIRED FOR VERIFY)
;
;	(AH)=2	READ THE DESIRED SECTORS INTO MEMORY
;	(AH)=3	WRITE THE DESIRED SECTORS FROM MEMORY
;	(AH)=4	VERIFY THE DESIRED SECTORS
;	(AH)=5	FORMAT THE DESIRED TRACK
;		FOR THE FORMAT OPERATION. THE BUFFER POINTER (ES,BX) MUST
;		POINT TO THE COLLECTION OF DESIRED ADDRESS FIELDS FOR THE
;		TRACK.	EACH FIELD IS COMPOSED OF 4 BYTES, (C,H,R,N), WHERE
;		C = TRACK NUMBER, H=HEAD NUMBER, R = SECTOR NUMBER, N= NUMBER
;		OF BYTES PER SECTOR (00=128, 01=256, 02=512. 03=1024,)
;		THERE MUST BE ONE ENTRY FOR EVERY SECTOR ON THE TRACK.
;		THIS INFORMATION IS USED TO FIND THE REQUESTED SECTOR DURING
;		READ/WRITE ACCESS.
;		PRIOR TO FORMATTING A DISKETTE, FUNCTION CALL 17 OF THIS
;		ROUTINE MUST BE INVOKED TO SET THE DISKETTE TYPE THAT IS TO
;		BE FORMATTED.
;		IN ORDER TO FORMAT 320/360K MEDIA IN EITHER A 320/360K OR
;		1.2M DISKETTE DRIVE THE GAP LENGTH FOR FORMAT PARAMETER
;		OF DISK_BASE MUST BE CHANGE TO 050H.  ALSO THE EOT
;		PARAMETER (LAST SECTOR ON TRACK) MUST BE SET TO THE
;		DESIRED NUMBER OF SECTORS/TRACK - 8 FOR 320K, 9 FOR 360K.
;		DISK_BASE IS POINTED TO BY DISK POINTER LOCATED AT
;		ABSOLUTE ADDRESS 0:78.
;		WHEN 320/360K FORMAT OPERATIONS ARE COMPLETE, THE PARAMETERS
;		SHOULD BE RESTORED TO THEIR RESPECTIVE INITIAL VALUES.
;	(AH)=15 READ DASD TYPE
;	REGISTERS
;	(AH) - ON RETURN IF CARRY FLAG NOT SET, OTHERWISE ERROR
;		 00 - DRIVE NOT PRESENT
;		 01 - DISKETTE, NO CHANGE LINE AVAILABLE
;		 02 - DISKETTE, CHANGE LINE AVAILABLE
;		 03 - FIXED DISK
;	(DL) - DRIVE NUMBER (0-1 ALLOWED. VALUE CHECKED)
;
;	(AH)=16 DISK CHANGE LINE STATUS
;	REGISTERS
;	(AH)=00 - DISK CHANGE LINE NOT ACTIVE
;	     06 - DISK CHANGE LINE ACTIVE & CARRY BIT ON
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;
;	(AH)=17 SET DASD TYPE FOR FORMAT
;	REGISTERS
;	(AL) -	 00 - NOT USED
;		 01 - DISKETTE 320/360K IN 360K DRIVE
;		 02 - DISKETTE 360K IN 1.2M DRIVE
;		 03 - DISKETTE 1.2M IN 1.2M DRIVE
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED;
;		DO NOT USE WHEN DISKETTE ATTACH CARD USED)
;	DISK CHANGE STATUS IS ONLY CHECKED WHEN A 1.2M BYTE DISKETTE
;	DRIVE IS SPECIFIED.  IF THE DISK CHANGE LINE IS FOUND TO BE
;	ACTIVE THE FOLLOWING ACTIONS TAKE PLACE:
;		ATTEMPT TO RESET DISK CHANGE LINE TO INACTIVE STATE.
;		IF ATTEMPT SUCCEEDS SET DASD TYPE FOR FORMAT AND RETURN DISK
;		CHANGE ERROR CODE
;		IF ATTEMPT FAILS RETURN TIMEOUT ERROR CODE AND SET DASD TYPE
;		TO A PREDETERMINED STATE INDICATING MEDIA TYPE UNKNOWN.
;	IF THE DISK CHANGE LINE IN INACTIVE PERFORM SET DASD TYPE FOR FORMAT.
;
; DATA VARIABLE -- DISK_POINTER
;	DOUBLE WORD POINTER TO THE CURRENT SET OF DISKETTE PARAMETERS
; OUTPUT
;	AH = STATUS OF OPERATION
;		STATUS BITS ARE DEFINED IN THE EQUATES FOR DISKETTE_STATUS
;		VARIABLE IN THE DATA SEGMENT OF THIS MODULE
;	CY = 0	SUCCESSFUL OPERATION (AH=0 ON RETURN, EXCEPT FOR READ DASD
;		TYPE AH=(15)).
;	CY = 1	FAILED OPERATION (AH HAS ERROR REASON)
;	FOR READ/WRITE/VERIFY
;		DS,BX,DX,CH,CL PRESERVED
;	NOTE: IF AN ERROR IS REPORTED BY THE DISKETTE CODE, THE APPROPRIATE
;		ACTION IS TO RESET THE DISKETTE, THEN RETRY THE OPERATION.
;		ON READ ACCESSES, NO MOTOR START DELAY IS TAKEN, SO THAT
;		THREE RETRIES ARE REQUIRED ON READS TO ENSURE THAT THE
;		PROBLEM IS NOT DUE TO MOTOR START-UP.
;-------------------------------------------------------------
;
;	DISKETTE STATE MACHINE - ABSOLUTE ADDRESS 40:90 & 91
;	 (DRIVE 0 - 90, DRIVE 1 - 91)
;	BITS
;
;   -----------------------------------------------------------------
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   |	7   |	6   |	5   |	4   |	3   |	2   |	1   |	0   |
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   -----------------------------------------------------------------
;	|	|	|	|	|	|	|	|
;	|	|	|	|	|	-----------------
;	|	|	|	|	|		|
;	|	|	|	|    RESERVED		--PRESENT STATE
;	|	|	|	|
;	|	|	|	|	 000: 360K IN 360K DRIVE UNESTABLISHED
;	|	|	|	|	 001: 360K IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	 002: 1.2M IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	 003: 360K IN 360K DRIVE ESTABLISHED
;	|	|	|	|	 004: 360K IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|	 005: 1.2M IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|
;	|	|	|	------> MEDIA/DRIVE ESTABLISHED
;	|	|	|
;	|	|	--------------> DOUBLE STEPPING REQUIRED (360K IN 1.2M
;	|	|			 DRIVE)
;	|	|
;	------------------------------> DATA TRANSFER RATE FOR THIS DRIVE:
;
;						00: 500 KBS
;						01: 300 KBS
;						10: 250 KBS
;						11: RESERVED
;
;	STATE OPERATION STARTED - ABSOLUTE ADDRESS 40:92 & 93
;	 (DRIVE 0 - 92, DRIVE 1 - 93)
;	PRESENT CYLINDER NUMBER - ABSOLUTE ADDRESS 40:94 & 95
;	 (DRIVE 0 - 94, DRIVE 1 - 95)
;-------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
PUBLIC	DISKETTE_IO_1
DISKETTE_IO_1	PROC	FAR	;>>> ENTRY POINT FOR ORG 0EC59H
	STI			; INTERRUPTS BACK ON
	PUSH	BX		; SAVE ADDRESS
	PUSH	CX
	PUSH	DS		; SAVE SEGMENT REGISTER VALUE
	PUSH	SI		; SAVE ALL REGISTERS DURING OPERATION
	PUSH	DI
	PUSH	BP
	PUSH	DX
	MOV	BP,SP		; SET UP POINTER TO HEAD PARM
	MOV	SI,DATA
	MOV	DS,SI		; SET DATA REGION
	CMP	AH,1		; CHECK FOR RESET AND STATUS OPERATIONS
	JBE	R4		; BYPASS DRIVE CHECK IF YES
;
	CMP	DL,1		; CHECK DRIVE NUMBER FOR VALIDITY
	JBE	R4		; IF VALID CONTINUE
;
R5:	MOV	DISKETTE_STATUS,BAD_CMD ; INVALID DRIVE ADDRESS, TERMINATE
	MOV	SI,0		; INSURE THAT RETURN STATUS GETS SETUP
	JMP	SHORT OK	; GO TERMINATE COMMAND
;
R4:	PUSH	AX		; SAVE ORIGINAL OPERATION FOR RETRY LATER ON
	CALL	J1		; CALL THE REST TO ENSURE DS RESTORE
	POP	SI		; RESTORE ORIGINAL OPERATION FOR RETRY
	MOV	DX,SI		; GET ORIGINAL OPERATION FOR TESTING
	CMP	DH,1		; SEE IF IT IS A RESET OR STATUS OPERATION
	JBE	OK		; BYPASS STATE UPDATE
;
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	OK		; DISKETTE ATTACH CARD
;
	CMP	DH,15H		; READ DISK CHANGE STATUS OR DISK TYPE COMMAND
	JAE	OK		; IF YES, BYPASS STATE PROCESSING
;
	MOV	DX,[BP] 	; RESTORE DRIVE PARAMETER
	XOR	BH,BH		; SETUP ADDRESS TO MEDIA STATE FOR THIS DRIVE
	MOV	BL,DL		; *
	MOV	AH,DISKETTE_STATUS	; GET STATUS OF OPERATION
	OR	AH,AH		; SEE IF ANY ERRORS
	JNZ	RETRY		; JUMP TO CHECK FOR MEDIA CHANGE
;
	MOV	AH,DSK_STATE[BX] ; GET MEDIA STATE OF DRIVE
	TEST	AH,DETERMINED	; SEE IF MEDIA STATE SET ALREADY
	JNZ	OK2		; IF SET, DONT CHANGE STATE
;
	MOV	CL,AH		; GET PRESENT STATE
	AND	CL,STATE_MSK	; ISOLATE STATE NUMBER
	ADD	CL,3		; ELEVATE STATE TO SET ALREADY
	AND	AH,REV_STATE	; CLEAR OUT STATE NUMBER
	OR	AH,CL		; SET NEW STATE NUMBER
	OR	AH,DETERMINED	; MAKE MEDIA STATE SET
	MOV	DSK_STATE[BX],AH ; SAVE IN DRIVE STATE INDICATOR
OK2:	MOV	DSK_STATE[BX+2],0 ; CLEAR ORIGINAL STATE OPERATION STARTED IN
OK:	MOV	BX,4		; GET THE MOTOR WAIT PARAMETER
	MOV	DX,SI		; GET ORIGINAL OP AGAIN
	PUSH	AX		; SAVE RETURN VALUE
	CALL	GET_PARM
	MOV	MOTOR_COUNT,AH	; SET THE TIMER COUNT FOR THE MOTOR
	POP	AX		; RESTORE RETURN VALUE
	CMP	DH,015H 	; SEE IF READ DASD OPERATION
	JNE	R20		; IF NOT BYPASS
;
	XCHG	AH,AL		; PUT RESULT IN AH
	CLC			; SET SUCCESSFUL OPERATION
	JMP	SHORT R19	; GO LEAVE
;
R20:	MOV	AH,DISKETTE_STATUS	; GET STATUS OF OPERATION
	CMP	AH,1		; SET THE CARRY FLAG TO INDICATE
	CMC			;  SUCCESS OR FAILURE
R19:	POP	DX		; RESTORE ALL REGISTERS
	POP	BP
	POP	DI
	POP	SI
	POP	DS
	POP	CX
	POP	BX		; RECOVER ADDRESS
	RET	2		; THROW AWAY SAVED FLAGS
;
RETRY:	CMP	DISKETTE_STATUS,MEDIA_CHANGE ; GET STATUS OF OPERATION
	JE	OK1		; TRUE ERROR DONT RETRY
;
	MOV	AH,DSK_STATE[BX] ; GET MEDIA STATE OF DRIVE
	AND	AH,STATE_MSK	; ISOLATE STATE
	CMP	AH,3		; SEE IF IN STATE 3
	JAE	OK2		; IF ESTABLISHED STATE THEN TRUE ERROR
;
;------ HANDLE STATES 0, 1 & 2
;
	INC	AH		; TRY NEXT STATE
	CMP	AH,3		; SEE IF OVERFLOW IN NON-ESTABLISHED STATES
	JNE	R2		; SKIP RESET TO BEGINNING IF YES
;
	MOV	AH,0		; NEXT STATE TO TRY AFTER OVERFLOW
R2:	MOV	CH,DSK_STATE[BX+2] ; GET START RETRY STATE
	AND	CH,STATE_MSK	; ISOLATE STATE BITS
	CMP	CH,AH		; ALL STATES TRIED
	JE	OK3		; IF YES, THEN TRUE ERROR
;
;------ SETUP STATE INDICATOR FOR RETRY ATTEMPT
;
	MOV	CH,DSK_STATE[BX] ; GET STATE INDICATOR
	ROL	CH,1		; MOVE TRANSFER RATE TO LOW ORDER BITS
	ROL	CH,1		; *
	AND	CH,TRAN_MSK	; ISOLATE TRANSFER RATE BITS
	DEC	CH		; CONVERT TO NEXT RATE
	CMP	CH,0FFH 	; SEE IF OVERFLOW OCCURRED
	JNE	R3		; JUMP IF NO OVERFLOW
;
	MOV	CH,XRATE	; SET TO NEXT RATE
R3:	ROR	CH,1		; PUT TRANSFER BITS BACK WHERE THEY BELONG
	ROR	CH,1		; *
	CMP	AH,1		; SEE IF THIS STATE REQUIRES DOUBLE STEP
	JNE	R9		; IF NOT, BYPASS SETTING DOUBLE STEP
;
	OR	CH,DOUBLE_STEP	; TURN ON DOUBLE STEP REQUIRED
R9:	OR	AH,CH		; COMBINE WITH STATE TO MAKE NEW INDICATOR
	MOV	DSK_STATE[BX],AH ; SAVE AS NEW INDICATOR
;
;------ SETUP FOR ACTUAL RETRY OPERATION
;
	MOV	DX,[BP] 	; RESTORE PARAMETERS FROM STACK
	MOV	CX,[BP+10]	; *
	MOV	BX,[BP+12]	; *
	MOV	AX,SI		; *
	JMP	R4		; GO RETRY OPERATION
;
OK1:	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	CALL	READ_DSKCHNG	; GO READ DISK CHANGE LINE STATUS
	JNZ	OK4		; IF ACTIVE, NO DISKETTE IN DRIVE, TIMEOUT
;
	JMP	OK2		; IF NOT ACTIVE, DISKETTE IN DRIVE, DISK CHANGE
;
OK4:	MOV	DISKETTE_STATUS,TIME_OUT ; INDICATE TIMEOUT IF DRIVE EMPTY
	JMP	OK2
;
OK3:	MOV	DSK_STATE[BX],POA_START ; ERROR PUT STATE AT POWER ON ASSUMPTION
	JMP	OK2
;
DISKETTE_IO_1	ENDP
;
;------ DETERMINE NEW MEDIA TYPE, NEED TO RESET DISK CHANGE LINE HERE
;
J1	PROC	NEAR
	CMP	AH,1		; TEST FOR RESET AND STATUS OPERATION
	JBE	J1E		; BYPASS STATE CHECK AND UPDATE
;
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	J1A		; DISKETTE ATTACH CARD
;
	CMP	AH,15H		; TEST FOR CHANGE STATUS OR DISK TYPE
	JAE	J1E		; BYPASS STATE CHECK AND UPDATE
;
	PUSH	AX		; SAVE ORIGINAL PARAMETERS
	PUSH	BX		; SAVE PARAMETERS
	PUSH	CX		; *
	PUSH	DX		; *
	CALL	READ_DSKCHNG	; GO READ DISK CHANGE LINE STATE
	JZ	J1I		; BYPASS HANDLING DISK CHANGE LINE
;
	JMP	J1F		; HANDLE DISK CHANGE LINE ACTIVE
;
J1A:	PUSH	AX		; SAVE ORIGINAL PARAMETERS
	PUSH	BX		; SAVE PARAMETERS
	PUSH	CX		; *
	PUSH	DX		; *
	CALL	READ_DSKCHNG	; SELECT DRIVE FOR DISKETTE ATTACH CARD
	JMP	SHORT J1H	; IGNORE DISK CHANGE STATUS
;
J1I:	MOV	AL,DSK_STATE[BX] ; GET MEDIA STATE INFORMATION FOR DRIVE
	OR	AL,AL		; CHECK FOR NO STATE INFORMATION AT ALL
	JNZ	J1D		; IF INFORMATION DONT DEFAULT
;
	MOV	AL,POA_START	; GET DEFAULT TO STATE 0
	MOV	DSK_STATE[BX],AL ; SET UP DEFAULT STATE 1
;
J1D:	CMP	AL,POA_DUAL	; SEE IF DOUBLE STEP RATE
	JNE	J1G		; BYPASS TRACK CHECK
;
	MOV	CX,[BP+10]	; GET ORIGINAL TRACK PARAMETER
	CMP	CH,40		; SEE IF TRACK IS PAST END OF DISKETTE(320)
	JB	J1G		; GO TRY OPERATION AT THIS STATE IF NOT
;
	MOV	DSK_STATE[BX],02H ; SET NEXT STATE TO TRY IN ALGORITHM
	MOV	AL,02H		; PUT NEW STATE IN WORKING REGISTER
	MOV	DH,DSK_STATE[BX+2] ; GET OPERATION START STATE
	OR	DH,DH		; CHECK FOR OPERATION START
	JNZ	J1C		; IF STARTED PREVIOUSLY, BYPASS SETTING IT UP
;
	MOV	DSK_STATE[BX+2],POA_DUAL ; SETUP STARTING STATE
	JMP	SHORT J1C	; BYPASS NEXT STEP ALREADY DONE
;
J1G:	MOV	DL,DSK_STATE[BX+2] ; GET START MEDIA STATE
	OR	DL,DL		; SEE IF THIS IS ORIGINAL OPERATION OR A RETRY
	JNZ	J1C		; IF RETRY IGNORE
;
	MOV	DSK_STATE[BX+2],AL ; SAVE AS STARTING DATA RATE
J1C:	MOV	CL,LASTRATE	; GET LAST DATA RATE SELECTED
	CMP	AL,CL		; COMPARE TO LAST OPERATION
	JE	J1H		; IF SAME DONT SELECT NEW TRANSFER RATE
;
	MOV	LASTRATE,AL	; SAVE NEW TRANSFER RATE FOR NEXT CHECK
	ROL	AL,1		; MOVE TRANSFER RATE DATA TO LOW BITS
	ROL	AL,1		; *
	AND	AL,TRAN_MSK	; CLEAR ALL BITS BUT DATA TRANSFER RATE BITS
	MOV	DX,03F7H	; ADDRESS FLOPPY CONTROL REGISTER
	OUT	DX,AL		; SET DATA TRANSFER RATE
J1H:	POP	DX		; RESTORE PARAMETERS
	POP	CX		; *
	POP	BX		; *
	POP	AX		; *
J1E:	MOV	DH,AL		; SAVE SECTOR # IN DH
	AND	MOTOR_STATUS,07FH	; INDICATE A READ OPERATION
	OR	AH,AH		; AH=0
	JZ	DISK_RESET
	DEC	AH		; AH=1
	JZ	DISK_STATUS
	MOV	DISKETTE_STATUS,0	; RESET THE STATUS INDICATOR
	DEC	AH		; AH=2
	JZ	DISK_READ
	DEC	AH		; AH=3
	JNZ	J2		; TEST_DISK_VERF
	JMP	DISK_WRITE
J2:				; TEST_DISK_VERF
	DEC	AH		; AH=4
	JZ	DISK_VERF
	DEC	AH		; AH=5
	JZ	DISK_FORMAT
	SUB	AH,10H		; AH=15H
	JNZ	J3		; BYPASS DISK TYPE OPERATION
;
	JMP	DISK_TYPE	; GO PERFORM DISK TYPE OPERATION

J3:	DEC	AH		; AH=16H
	JNZ	J4		; BYPASS DISK CHANGE STATUS
;
	JMP	DISK_CHANGE	; GO CHECK DISK CHANGE LINE STATUS
;
J4:	DEC	AH		; AH=17H
	JNZ	J5		; BAD COMMAND
;
	JMP	FORMAT_SET	; GO SET MEDIA/DRIVE TYPE FOR FORMAT
;
J5:	MOV	DISKETTE_STATUS,BAD_CMD ; ERROR CODE, NO SECTORS TRANSFERRED
	RET			; UNDEFINED OPERATION
J1	ENDP

;------ RESET THE DISKETTE SYSTEM

DISK_RESET	PROC	NEAR
	MOV	DX,03F2H	; ADAPTER CONTROL PORT
	CLI			; NO INTERRUPTS
	MOV	AL,MOTOR_STATUS ; WHICH MOTOR IS ON
	AND	AL,3FH		; STRIP OFF UNWANTED BITS
	MOV	CL,4		; SHIFT COUNT
	ROL	AL,CL		; MOVE MOTOR VALUE TO HIGH NIBBLE, DRIVE SELECT
				;  TO LOW NIBBLE
	OR	AL,8		; TURN ON INTERRUPT ENABLE
	OUT	DX,AL		; RESET THE ADAPTER
	MOV	SEEK_STATUS,0	; SET RECAL REQUIRED ON ALL DRIVES
	MOV	DISKETTE_STATUS,0 ; SET OK STATUS FOR DISKETTE
	JMP	$+2		; I/O WAIT STATE
	OR	AL,4		; TURN OFF RESET
	OUT	DX,AL		; TURN OF THE RESET
	STI			; REENABLE THE INTERRUPTS
	CALL	CHK_STAT_2	; DO SENSE INTERRUPT STATUS FOLLOWING RESET
	MOV	AL,NEC_STATUS	; IGNORE ERROR RETURN AND DO OWN TEST
	CMP	AL,0C0H 	; TEST FOR DRIVE READY TRANSITION
	JZ	J7		; EVERYTHING OK
	OR	DISKETTE_STATUS,BAD_NEC ; SET ERROR CODE
	RET

;------ SEND SPECIFY COMMAND TO NEC

J7:				; DRIVE_READY
	MOV	AH,03H		; SPECIFY COMMAND
	CALL	NEC_OUTPUT	; OUTPUT THE COMMAND
	MOV	BX,1		; FIRST BYTE PARM IN BLOCK
	CALL	GET_PARM	;  TO THE NEC CONTROLLER
	MOV	BX,3		; SECOND BYTE PARM IN BLOCK
	CALL	GET_PARM	;  TO THE NEC CONTROLLER
				; RESET_RE
	RET			; RETURN TO CALLER
DISK_RESET	ENDP

;------ DISKETTE STATUS ROUTINE

DISK_STATUS	PROC	NEAR
	RET
DISK_STATUS	ENDP

;------ DISKETTE READ

DISK_READ	PROC	NEAR
	MOV	AL,046H 	; READ COMMAND FOR DMA
J9:				; DISK_READ_CONT
	CALL	DMA_SETUP	; SET UP THE DMA
	MOV	AH,0E6H 	; SET UP READ COMMAND FOR NEC CONTROLLER
	JMP	SHORT RW_OPN	; GO DO THE OPERATION
DISK_READ	ENDP

;------ DISKETTE VERIFY

DISK_VERF	PROC	NEAR
	MOV	AL,042H 		; VERIFY COMMAND FOR DMA
	JMP	J9			; DO AS IF DISK READ
DISK_VERF	ENDP

;------ DISKETTE FORMAT

DISK_FORMAT	PROC	NEAR
	OR	MOTOR_STATUS,WRITE_OP	;   INDICATE WRITE OPERATION
	MOV	AL,04AH 		;  WILL WRITE TO DISKETTE
	CALL	DMA_SETUP		; SET UP THE DMA
	MOV	AH,04DH 		; ESTABLISH THE FORMAT COMMAND
	JMP	SHORT RW_OPN		; DO THE OPERATION
J10:					; CONTINUATION OF RW_OPN FOR FMT
	MOV	BX,7			; GET THE
	CALL	GET_PARM		;  BYTES/SECTOR VALUE TO NEC
	MOV	BX,9			; GET THE
	CALL	GET_PARM		;  SECTORS/TRACK VALUE TO NEC
	MOV	BX,15			; GET THE
	CALL	GET_PARM		;  GAP LENGTH VALUE TO NEC
	MOV	BX,17			; GET THE FILLER BYTE
	JMP	J16			;  TO THE CONTROLLER
DISK_FORMAT	ENDP

;------ DISKETTE WRITE ROUTINE

DISK_WRITE	PROC	NEAR
	OR	MOTOR_STATUS,WRITE_OP	; INDICATE WRITE OPERATION
	MOV	AL,04AH 		; DMA WRITE COMMAND
	CALL	DMA_SETUP
	MOV	AH,0C5H 		; NEC COMMAND TO WRITE TO DISKETTE
DISK_WRITE	ENDP
;----- ALLOW WRITE ROUTINE TO FALL INTO RW_OPN
;---------------------------------------
; RW_OPN
;	THIS ROUTINE PERFORMS THE READ/WRITE/VERIFY OPERATION
;---------------------------------------
RW_OPN	PROC	NEAR
	JNC	J11			; TEST FOR DMA ERROR
	MOV	DISKETTE_STATUS,DMA_BOUNDARY	; SET ERROR
	MOV	AL,0			; NO SECTORS TRANSFERRED
	RET				; RETURN TO MAIN ROUTINE
J11:					; DO_RW_OPN
	PUSH	AX			; SAVE THE COMMAND

;------ TURN ON THE MOTOR AND SELECT THE DRIVE

	PUSH	CX			; SAVE THE T/S PARMS
	MOV	CL,DL			; GET DRIVE NUMBER AS SHIFT COUNT
	MOV	AL,1			; MASK FOR DETERMINING MOTOR BIT
	SAL	AL,CL			; SHIFT THE MASK BIT
	CLI				; NO INTERRUPTS WHILE DETERMINING MOTOR STATUS
	TEST	AL,MOTOR_STATUS 	; IS THIS MOTOR ON
	JZ	R13			; IF NOT GO TEST FOR WAIT NECESSARY

	CMP	MOTOR_COUNT,0ECH	; SEE IF THE MOTOR HAS BEEN ON LONG ENOUGH
	MOV	MOTOR_COUNT,0FFH	; ENSURE MOTOR DOESNT TURN OFF DURING OPERATION
	JB	J14			; IS LESS THAN EC, THEN TURN ON NOT DUE TO
					;  READING OF DISK CHANGE LINE, OTHERWISE
					;  GO TEST FOR WAIT NECESSARY
;
R13:	OR	MOTOR_STATUS,AL 	; TURN ON THE CURRENT MOTOR
	MOV	CL,4			; SHIFT COUNT TO MOVE DRIVE TO HIGH NIBBLE
	AND	MOTOR_STATUS,0CFH	; CLEAR ENCODED DRIVE SELECT BITS(4 & 5)
	ROL	DL,CL			; MOVE DRIVE ENCODED BITS TO HIGH NIBBLE
	OR	MOTOR_STATUS,DL 	; SAVE AS SELECTED DRIVE
	ROR	DL,CL			; RESTORE
	STI				; INTERRUPTS BACK ON
	MOV	AL,MOTOR_STATUS 	; GET MOTORS ON AND DRIVE SELECTED
	AND	AL,03FH 		; STRIP OFF UNWANTED BITS
	ROL	AL,CL			; SHIFT BITS AROUND TO DESIRED POSITIONS
	OR	AL,0CH			; NO RESET, ENABLE DMA/INT
	PUSH	DX			; SAVE REG
	MOV	DX,03F2H		; CONTROL PORT ADDRESS
	OUT	DX,AL
	POP	DX			; RECOVER REGISTERS

;------ WAIT FOR MOTOR
;
	CLC				; CLEAR TIMEOUT INDICATOR
	MOV	AX,090FDH		; LOAD WAIT CODE & TYPE
	INT	15H			; PERFORM OTHER FUNCTION
	JC	J14			; BYPASS TIMING LOOP IF TIMEOUT OCCURRED
;
	MOV	BX,20			; GET THE MOTOR WAIT
	CALL	GET_PARM		;  PARAMETER
	OR	AH,AH			; TEST FOR NO WAIT
J12:					; TEST_WAIT_TIME
	JZ	J14			; EXIT WITH TIME EXPIRED
	SUB	CX,CX			; SET UP 1/8 SECOND LOOP TIME
J13:	LOOP	J13			; WAIT FOR THE REQUIRED TIME
;
	MOV	CX,06D06H		; *
R18:	LOOP	R18			; *
;
	DEC	AH			; DECREMENT TIME VALUE
	JNZ	J12			; ARE WE DONE YET

J14:					; MOTOR_RUNNING
	STI				; INTERRUPTS BACK ON FOR BYPASS WAIT
	POP	CX

;------ DO THE SEEK OPERATION

	CALL	SEEK		; MOVE TO CORRECT TRACK
	POP	AX		; RECOVER COMMAND
	MOV	BH,AH		; SAVE COMMAND IN BH
	MOV	DH,0		; SET NO SECTORS READ IN CASE OF ERROR
	JC	J17		; IF ERROR, THEN EXIT AFTER MOTOR OFF
	MOV	SI,OFFSET J17	; DUMMY RETURN ON STACK FOR NEC_OUTPUT
	PUSH	SI		;  SO THAT IT WILL RETURN TO MOTOR OFF LOCATION

;------ SEND OUT THE PARAMETERS TO THE CONTROLLER

	CALL	NEC_OUTPUT	; OUTPUT THE OPERATION COMMAND
	MOV	AH,[BP+1]	; GET THE CURRENT HEAD NUMBER
	SAL	AH,1		; MOVE IT TO BIT 2
	SAL	AH,1
	AND	AH,4		; ISOLATE THAT BIT
	OR	AH,DL		; OR IN THE DRIVE NUMBER
	CALL	NEC_OUTPUT

;------ TEST FOR FORMAT COMMAND

	CMP	BH,04DH 	; IS THIS A FORMAT OPERATION
	JNE	J15		; NO, CONTINUE WITH R/W/V
	JMP	J10		; IF SO, HANDLE SPECIAL

J15:	MOV	AH,CH		; CYLINDER NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,[BP+1]	; HEAD NUMBER FROM STACK
	CALL	NEC_OUTPUT
	MOV	AH,CL		; SECTOR NUMBER
	CALL	NEC_OUTPUT
	MOV	BX,7		; BYTES/SECTOR PARM FROM BLOCK
	CALL	GET_PARM	;  TO THE NEC
	MOV	BX,9		; EOT PARM FROM BLOCK
	CALL	GET_PARM	;  TO THE NEC
	MOV	BX,[BP] 	; RESTORE DRIVE NUMBER FROM PARMS
	XOR	BH,BH		; CLEAR HIGH ORDER INDEX REGISTER
	MOV	AH,DSK_STATE[BX] ; GET DRIVE STATE VALUE
	TEST	AH,DETERMINED	; SEE IF STATE ALREADY ESTABLISHED
	JZ	D0		; BYPASS STATE REDUCTION FOR GAP LENGTH
;
	AND	AH,07H		; STRIP OFF HIGH BITS
	SUB	AH,03H		; REDUCE STATES
;
D0:	AND	AH,07H		; STRIP OFF HIGH BITS
	CMP	AH,0		; CHECK FOR DISKETTE ATTACH CARD OR 320 DRIVE
	JNE	R16		; IF NOT CHECK FOR NEXT STATE
;
	MOV	AH,02AH 	; LOAD 320/360 DRIVE GAP LENGTH
	JMP	SHORT R15	;
;
R16:	CMP	AH,1		; CHECK FOR 320 MEDIA IN 1.2 DRIVE
	JNE	R17		; IF NOT, THEN HANDLE 1.2 MEDIA IN 1.2 DRIVE
;
	MOV	AH,023H 	; LOAD 320/360 MEDIA IN 1.2 DRIVE GAP LENGTH
	JMP	SHORT R15	;
;
R17:	MOV	AH,01BH 	; LOAD 1.2 MEDIA IN 1.2 DRIVE GAP LENGTH
R15:	CALL	NEC_OUTPUT
	MOV	BX,13		; DTL PARM FROM BLOCK
J16:				; RW_OPN FINISH
	CALL	GET_PARM	;  TO THE NEC
	POP	SI		; CAN NOW DISCARD THAT DUMMY
				;  RETURN ADDRESS

;------ LET THE OPERATION HAPPEN

	CALL	WAIT_INT	; WAIT FOR THE INTERRUPT
J17:				; MOTOR_OFF
	JC	J21		; LOOK FOR ERROR
	CALL	RESULTS 	; GET THE NEC STATUS
	JC	J20		; LOOK FOR ERROR

;------ CHECK THE RESULTS RETURNED BY THE CONTROLLER

	CLD			; SET THE CORRECT DIRECTION
	MOV	SI,OFFSET NEC_STATUS	; POINT TO STATUS FIELD
	LODS	NEC_STATUS		; GET ST0
	AND	AL,0C0H 		; TEST FOR NORMAL TERMINATION
	JZ	J22			; OPN_OK
	CMP	AL,040H 		; TEST FOR ABNORMAL TERMINATION
	JNZ	J18			; NOT ABNORMAL, BAD NEC

;------ ABNORMAL TERMINATION, FIND OUT WHY

	LODS	NEC_STATUS		; GET ST1
	SAL	AL,1			; TEST FOR EOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1			; TEST FOR CRC ERROR
	MOV	AH,BAD_CRC
	JC	J19			; RW_FAIL
	SAL	AL,1			; TEST FOR DMA OVERRUN
	MOV	AH,BAD_DMA
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1			; TEST FOR RECORD NOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	MOV	AH,WRITE_PROTECT	; TEST FOR WRITE PROTECT
	JC	J19			; RW_FAIL
	SAL	AL,1			; TEST MISSING ADDRESS MARK
	MOV	AH,BAD_ADDR_MARK
	JC	J19			; RW_FAIL

;------ NEC MUST HAVE FAILED

J18:					; RW-NEC-FAIL
	MOV	AH,BAD_NEC
J19:					; RW-FAIL
	OR	DISKETTE_STATUS,AH
	CALL	NUM_TRANS		; HOW MANY WERE REALLY TRANSFERRED
J20:					; RW_ERR
	RET			; RETURN TO CALLER

J21:				; RW_ERR_RES
	CALL	RESULTS 	; FLUSH THE RESULTS BUFFER
	RET

;------ OPERATION WAS SUCCESSFUL

J22:					; OPN_OK
	CALL	NUM_TRANS		; HOW MANY GOT MOVED
	XOR	AH,AH			; NO ERRORS
	RET
RW_OPN	ENDP
;-----------------------------------------------
; GET_PARM
;  THIS ROUTINE FETCHES THE INDEXED POINTER FROM
;  THE DISK_BASE BLOCK POINTED AT BY THE DATA
;  VARIABLE DISK_POINTER
; A BYTE FROM THAT TABLE IS THEN MOVED INTO AH,
;  THE INDEX OF THAT BYTE BEING THE PARM IN BX
; ENTRY --
;   BX = INDEX OF BYTE TO BE FETCHED * 2
;	 IF THE LOW BIT OF BX IS ON, THE BYTE IS IMMEDIATELY
;	 OUTPUT TO THE NEC CONTROLLER
; EXIT --
;   AH = THAT BYTE FROM BLOCK
;------------------------------------------------
GET_PARM	PROC	NEAR
	PUSH	DS			; SAVE SEGMENT
	PUSH	SI			; SAVE
	SUB	AX,AX			; ZERO TO AX
	MOV	DS,AX
	ASSUME	DS:ABS0
	LDS	SI,DISK_POINTER 	; POINT TO BLOCK
	SHR	BX,1			; DIVIDE BX BY 2, AND SET FLAG FOR EXIT
	MOV	AH,[SI+BX]		; GET THE WORD
	POP	SI			; RESTORE
	POP	DS			; RESTORE SEGMENT
	PUSHF				; SAVE RESULTS FOR EXIT
	ASSUME	DS:DATA
	CMP	BX,10			; LOOK FOR MOTOR STARTUP DELAY PARM
	JNE	GP0			; BYPASS IF NOT PARM LOOKING FOR
;
	TEST	MOTOR_STATUS,WRITE_OP	; IS THIS A WRITE
	JZ	GP1			; NO, ENFORCE MINIMUM READ WAIT
;
	CMP	AH,8			; SEE IF AT LEAST A SECOND SPECIFIED
	JAE	GP2			; IF YES, CONTINUE
;
	MOV	AH,8			; FORCE A SECOND WAIT FOR MOTOR START
	JMP	SHORT GP2		; CONTINUE
;
GP1:	CMP	AH,5			; SEE IF A 625 MS WAIT ON READ
	JAE	GP2			; IF YES, CONTINUE
;
	MOV	AH,5			; ENFORCE A 625 MS WAIT
	JMP	SHORT GP2		; CONTINUE
;
GP0:	CMP	BX,9			; IS THIS HEAD SETTLE PARM
	JNE	GP2			; BYPASS IF NOT HEAD SETTLE
;
	TEST	MOTOR_STATUS,WRITE_OP	; SEE IF A WRITE OPERATION
	JZ	GP2			; IF NOT, DONT ENFORCE ANY VALUES
;
	OR	AH,AH			; CHECK FOR ANY WAIT?
	JNZ	GP2			; IF THERE DONT ENFORCE
;
	PUSH	DX			; SAVE REGISTER
	PUSH	BX			; SAVE REGISTER
	MOV	DX,[BP] 		; GET ORIGINAL DRIVE REQUESTED
	XOR	BH,BH			; SET UP ADDRESSING TO STATE INDICATOR
	MOV	BL,DL			; *
	MOV	AH,HD12_SETTLE		; SPEC'ED HEAD SETTLE TIME FOR 1.2 DRIVE
	MOV	AL,DSK_STATE[BX]	; GET MEDIA/DRIVE STATE
	POP	BX			; RESTORE
	POP	DX			; RESTORE
	AND	AL,STATE_MSK		; ISOLATE STATE NUMBER
	JNZ	GP4			; BRANCH IF STATES 1 THRU 5
;
GP3:	MOV	AH,HD320_SETTLE 	; SPEC'ED HEAD SETTLE TIME FOR 320 DRIVE
	JMP	SHORT GP2		; GO TO WAIT LOOP
;
GP4:	CMP	AL,3			; SEE IF STATE 3(320 DRIVE/320 MEDIA)
	JE	GP3			; GO REESTABLISH WAIT TIME
;
GP2:	POPF				; RESTORE EXIT RESULTS
	JC	NEC_OUTPUT		; IF FLAG SET, OUTPUT TO CONTROLLER
	RET				; RETURN TO CALLER
GET_PARM	ENDP
;------------------------------------------------
; NEC_OUTPUT
;	THIS ROUTINE SENDS A BYTE TO THE NEC CONTROLLER
;	AFTER TESTING FOR CORRECT DIRECTION AND CONTROLLER READY
;	THIS ROUTINE WILL TIME OUT IF THE BYTE IS NOT ACCEPTED
;	WITHIN A REASONABLE AMOUNT OF TIME, SETTING THE DISKETTE STATUS
;	ON COMPLETION
; INPUT
;	(AH)	BYTE TO BE OUTPUT
; OUTPUT
;	CY = 0	SUCCESS
;	CY = 1	FAILURE -- DISKETTE STATUS UPDATED
;		IF A FAILURE HAS OCCURRED, THE RETURN IS MADE ONE LEVEL
;		HIGHER THAN THE CALLER OF NEC_OUTPUT
;		THIS REMOVES THE REQUIREMENT OF TESTING AFTER EVERY CALL
;		OF NEC_OUTPUT
;	(AL) DESTROYED
;------------------------------------------------
NEC_OUTPUT	PROC	NEAR
	PUSH	DX		; SAVE REGISTERS
	PUSH	CX
	PUSH	BX
	MOV	DX,03F4H	; STATUS PORT
	MOV	BL,2		; HIGH ORDER COUNTER
R11:	XOR	CX,CX		; COUNT FOR TIME OUT
J23:
	IN	AL,DX		; GET STATUS
	TEST	AL,040H 	; TEST DIRECTION BIT
	JZ	R12		; DIRECTION OK
	LOOP	J23
;
	DEC	BL		; DECREMENT COUNTER
	JNZ	R11		; REPEAT TIL DELAY FINISHED
;
J24:				; TIME_ERROR
	OR	DISKETTE_STATUS,TIME_OUT
	POP	BX		; RESTORE REGISTERS
	POP	CX
	POP	DX		; SET ERROR CODE AND RESTORE REGS
	POP	AX		; DISCARD THE RETURN ADDRESS
	STC			; INDICATE ERROR TO CALLER
	RET
R12:	MOV	BL,2		; HIGH ORDER COUNT
J25:
	XOR	CX,CX		; RESET THE COUNT
J26:
	IN	AL,DX		; GET THE STATUS
	TEST	AL,080H 	; IS IT READY
	JNZ	J27		; YES, GO OUTPUT
;
	LOOP	J26		; COUNT DOWN AND TRY AGAIN
;
	DEC	BL		; DECREMENT COUNTER
	JNZ	J25		; REPEAT TIL DELAY FINISHED
;
	JMP	J24		; ERROR CONDITION
J27:				; OUTPUT
	MOV	AL,AH		; GET BYTE TO OUTPUT
;	MOV	DX,03F5H	; DATA PORT
	MOV	DL,0F5H
	OUT	DX,AL		; OUTPUT THE BYTE
	POP	BX		; RECOVER REGISTERS
	POP	CX		; RECOVER REGISTERS
	POP	DX
	RET			; CY = 0 FROM TEST INSTRUCTION
NEC_OUTPUT	ENDP
;---------------------------------------------------
; SEEK
;	THIS ROUTINE WILL MOVE THE HEAD ON THE NAMED DRIVE
;	TO THE NAMED TRACK.  IF THE DRIVE HAS NOT BEEN ACCESSED
;	SINCE THE DRIVE RESET COMMAND WAS ISSUED, THE DRIVE WILL BE
;	RECALIBRATED.
; INPUT
;	(DL) = DRIVE TO SEEK ON
;	(CH) = TRACK TO SEEK TO
; OUTPUT
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- DISKETTE_STATUS SET ACCORDINGLY
;	(AX) DESTROYED
;------------------------------------------------------
SEEK	PROC	NEAR
	MOV	AL,1		; ESTABLISH MASK FOR RECAL TEST
	PUSH	CX		; SAVE INPUT VALUES
	MOV	CL,DL		; GET DRIVE VALUE INTO CL
	ROL	AL,CL		; SHIFT IT BY THE DRIVE VALUE
	POP	CX		; RECOVER TRACK VALUE
	TEST	AL,SEEK_STATUS	; TEST FOR RECAL REQUIRED
	JNZ	J28		; NO_RECAL
;
	OR	SEEK_STATUS,AL	; TURN ON THE NO RECAL BIT IN FLAG
	MOV	AH,07H		; RECALIBRATE COMMAND
	CALL	NEC_OUTPUT
	MOV	AH,DL
	CALL	NEC_OUTPUT	; OUTPUT THE DRIVE NUMBER
	CALL	CHK_STAT_2	; GET THE INTERUPT AND SENSE INT STATUS
	JNC	J28A		; SEEK_CONTINUE
;
;----- ISSUE RECALIBRATE FOR 80 TRACK DISKETTES
;
	MOV	DISKETTE_STATUS,0 ; CLEAR OUT INVALID STATUS
	MOV	AH,07H		; RECALIBRATE COMMAND
	CALL	NEC_OUTPUT
	MOV	AH,DL
	CALL	NEC_OUTPUT	; OUTPUT THE DRIVE NUMBER
	CALL	CHK_STAT_2	; GET THE INTERUPT AND SENSE INT STATUS
	JC	RB		; SEEK_ERROR
;
J28A:
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	J28		; DISKETTE ATTACH CARD
;
	XOR	BH,BH		; SET UP ADDRESSING TO STATE INDICATOR
	MOV	BL,DL		; *
	MOV	DSK_TRK[BX],0	; SAVE NEW CYLINDER AS PRESENT POSITION
;
;----- DRIVE IS IN SYNCH WITH CONTROLLER, SEEK TO TRACK

J28:
	XOR	BH,BH		; SET UP ADDRESSING TO STATE INDICATOR
	MOV	BL,DL		; *
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	R7		; DISKETTE ATTACH CARD
;
	TEST	DSK_STATE[BX],DOUBLE_STEP ; CHECK FOR DOUBLE STEP REQUIRED
	JZ	R7		; SINGLE STEP REQUIRED BYPASS DOUBLE
;
	SHL	CH,1		; DOUBLE NUMBER OF STEP TO TAKE
;
R7:	CMP	CH,DSK_TRK[BX]	; SEEK IF ALREADY AT THE DESIRED TRACK
	JE	J32		; IF YES, DONT NEED TO SEEK
;
	MOV	DSK_TRK[BX],CH	; SAVE NEW CYLINDER AS PRESENT POSITION
	MOV	AH,0FH		; SEEK COMMAND TO NEC
	CALL	NEC_OUTPUT
	MOV	AH,DL		; DRIVE NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,CH		; GET CYLINDER NUMBER
	CALL	NEC_OUTPUT
	CALL	CHK_STAT_2	; GET ENDING INTERRUPT AND SENSE STATUS
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	RA		; DISKETTE ATTACH CARD
;
	TEST	DSK_STATE[BX],DOUBLE_STEP ; CHECK FOR DOUBLE STEP REQUIRED
	JZ	RA		; SINGLE STEP REQUIRED BYPASS DOUBLE
;
	SHR	CH,1		; SET BACK TO LOGICAL SECTOR
RA:

;----- WAIT FOR HEAD SETTLE

	PUSHF			; SAVE STATE FLAGS
	MOV	BX,18		; GET HEAD SETTLE PARAMETER
	CALL	GET_PARM	; *
	PUSH	CX		; SAVE REGISTER
J29:				; HEAD_SETTLE
	MOV	CX,800		; 1 MS LOOP
	OR	AH,AH		; TEST FOR TIME EXPIRED
	JZ	J31
J30:	LOOP	J30		; DELAY FOR 1 MS
	DEC	AH		; DECREMENT THE COUNT
	JMP	J29		; DO IT SOME MORE
J31:
	POP	CX		; RECOVER STATE
	POPF
	RET			; RETURN TO CALLER
;
J32:				; SEEK ERROR
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	RB		; DISKETTE ATTACH CARD
;
	TEST	DSK_STATE[BX],DOUBLE_STEP ; CHECK FOR DOUBLE STEP REQUIRED
	JZ	RB		; SINGLE STEP REQUIRED BYPASS DOUBLE
;
	SHR	CH,1		; SET BACK TO LOGICAL SECTOR
RB:
	RET			; RETURN TO CALLER
SEEK	ENDP
;----------------------------------------------------
; DMA_SETUP
;	THIS ROUTINE SETS UP THE DMA FOR READ/WRITE/VERIFY
;	OPERATIONS.
; INPUT
;	(AL) = MODE BYTE FOR THE DMA
;	(ES:BX) - ADDRESS TO READ/WRITE THE DATA
; OUTPUT
;	(AX) DESTROYED
;----------------------------------------------------
DMA_SETUP	PROC	NEAR
	PUSH	CX		; SAVE THE REGISTER
	CLI			; DISABLE INTERRUPTS DURING DMA SET-UP
	OUT	DMA+12,AL	; SET THE FIRST/LAST F/F
	JMP	$+2		; WAIT FOR IO
	OUT	DMA+11,AL	; OUTPUT THE MODE BYTE
	MOV	AX,ES		; GET THE ES VALUE
	MOV	CL,4		; SHIFT COUNT
	ROL	AX,CL		; ROTATE LEFT
	MOV	CH,AL		; GET HIGHEST NYBBLE OF ES TO CH
	AND	AL,0F0H 	; ZERO THE LOW NYBBLE FROM SEGMENT
	ADD	AX,BX		; TEST FOR CARRY FROM ADDITION
	JNC	J33
	INC	CH		; CARRY MEANS HIGH 4 BITS MUST BE INC
J33:
	PUSH	AX		; SAVE START ADDRESS
	OUT	DMA+4,AL	; OUTPUT LOW ADDRESS
	JMP	$+2		; WAIT FOR IO
	MOV	AL,AH
	OUT	DMA+4,AL	; OUTPUT HIGH ADDRESS
	MOV	AL,CH		; GET HIGH 4 BITS
	JMP	$+2		; I/O WAIT STATE
	AND	AL,0FH
	OUT	081H,AL 	; OUTPUT THE HIGH 4 BITS TO PAGE REGISTER

;------ DETERMINE COUNT

	MOV	AH,DH		; NUMBER OF SECTORS
	SUB	AL,AL		;  TIMES 256 INTO AX
	SHR	AX,1		; SECTORS * 128 INTO AX
	PUSH	AX
	MOV	BX,6		; GET THE BYTES/SECTOR PARM
	CALL	GET_PARM
	MOV	CL,AH		; USE AS SHIFT COUNT (0=128, 1=256 ETC)
	POP	AX
	SHL	AX,CL		; MULTIPLY BY CORRECT AMOUNT
	DEC	AX		;-1 FOR DMA VALUE
	PUSH	AX		; SAVE COUNT VALUE
	OUT	DMA+5,AL	; LOW BYTE OF COUNT
	JMP	$+2		; WAIT FOR IO
	MOV	AL,AH
	OUT	DMA+5,AL	; HIGH BYTE OF COUNT
	STI			; RE-ENABLE INTERRUPTS
	POP	CX		; RECOVER COUNT VALUE
	POP	AX		; RECOVER ADDRESS VALUE
	ADD	AX,CX		; ADD, TEST FOR 64K OVERFLOW
	POP	CX		; RECOVER REGISTER
	MOV	AL,2		; MODE FOR 8237
	OUT	DMA+10,AL	; INITIALIZE THE DISKETTE CHANNEL
	RET			; RETURN TO CALLER, CFL SET BY ABOVE IF ERROR
DMA_SETUP	ENDP
;---------------------------------------------------
; CHK_STAT_2
;	THIS ROUTINE HANDLES THE INTERRUPT RECEIVED AFTER
;	A RECALIBRATE, SEEK, OR RESET TO THE ADAPTER.
;	THE INTERRUPT IS WAITED FOR, THE INTERRUPT STATUS SENSED,
;	AND THE RESULT RETURNED TO THE CALLER.
; INPUT
;	NONE
; OUTPUT
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- ERROR IS IN DISKETTE_STATUS
;	(AX) DESTROYED
;----------------------------------------------------
CHK_STAT_2	PROC	NEAR
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	J34			; IF ERROR, RETURN IT
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	CALL	RESULTS 		; READ IN THE RESULTS
	JC	J34			; CHK2_RETURN
	MOV	AL,NEC_STATUS		; GET THE FIRST STATUS BYTE
	AND	AL,060H 		; ISOLATE THE BITS
	CMP	AL,060H 		; TEST FOR CORRECT VALUE
	JZ	J35			; IF ERROR, GO MARK IT
	CLC				; GOOD RETURN
J34:
	RET				; RETURN TO CALLER
J35:					; CHK2_ERROR
	OR	DISKETTE_STATUS,BAD_SEEK
	STC				; ERROR RETURN CODE
	RET
CHK_STAT_2	ENDP

;--------------------------------------------
; WAIT_INT
;	THIS ROUTINE WAITS FOR AN INTERRUPT TO OCCUR
;	A TIME OUT ROUTINE TAKES PLACE DURING THE WAIT, SO
;	THAT AN ERROR MOV BE RETURNED IF THE DRIVE IS NOT READY
; INPUT
;	NONE
; OUTPUT
;	CY = 0 SUCCESS
;	CY = 1 FAILURE -- DISKETTE_STATUS IS SET ACCORDINGLY
;	(AX) DESTROYED
;--------------------------------------------
WAIT_INT	PROC	NEAR
	STI				; TURN ON INTERRUPTS, JUST IN CASE
	PUSH	AX			; SAVE REGISTERS
	PUSH	BX			; *
	PUSH	CX			; *
	CLC				; CLEAR TIMEOUT INDICATOR
	MOV	AX,09001H		; LOAD WAIT CODE AND TYPE
	INT	15H			; PERFORM OTHER PUNCTION
	JC	J36A			; BYPASS TIMING LOOP IF TIMEOUT OCCURRED
;
	MOV	BL,4			; CLEAR THE COUNTERS
	XOR	CX,CX			; FOR 2 SECOND WAIT
J36:
	TEST	SEEK_STATUS,INT_FLAG	; TEST FOR INTERRUPT OCCURRING
	JNZ	J37
	LOOP	J36			; COUNT DOWN WHILE WAITING
	DEC	BL			; SECOND LEVEL COUNTER
	JNZ	J36
;
J36A:	OR	DISKETTE_STATUS,TIME_OUT ; NOTHING HAPPENED
	STC				; ERROR RETURN
J37:
	PUSHF				; SAVE CURRENT CARRY
	AND	SEEK_STATUS,NOT INT_FLAG ; TURN OFF INTERRUPT FLAG
	POPF				; RECOVER CARRY
	POP	CX			; RECOVER REGISTERS
	POP	BX			; *
	POP	AX			; *
	RET				; GOOD RETURN CODE COMES FROM TEST INST
WAIT_INT	ENDP

;----------------------------------------------
; DISK_INT
;	THIS ROUTINE HANDLES THE DISKETTE INTERRUPT
; INPUT
;	NONE
; OUTPUT
;	THE INTERRUPT FLAG IS SET IN SEEK_STATUS
;-----------------------------------------------


DISK_INT_1	PROC	FAR		;>>> ENTRY POINT FOR ORG 0EF57H
	STI				; RE ENABLE INTERRUPTS
	PUSH	DS			; SAVE REGISTERS
	PUSH	AX			; *
	CALL	DDS			; SETUP DATA ADDRESSING
	OR	SEEK_STATUS,INT_FLAG	; TURN ON INTERRUPT OCCURRED
	MOV	AL,20H			; END OF INTERRUPT MARKER
	OUT	20H,AL			; INTERRUPT CONTROL PORT
	MOV	AX,09101H		; INTERRUPT POST CODE & TYPE
	INT	15H			; GO PERFORM OTHER TASK
	POP	AX			; RECOVER REG
	POP	DS			; *
	IRET				; RETURN FROM INTERRUPT
DISK_INT_1	ENDP
;----------------------------------------------
; RESULTS
;	THIS ROUTINE WILL READ ANYTHING THAT THE NEC CONTROLLER
;	HAS TO SAY FOLLOWING AN INTERRUPT.
; INPUT
;	NONE
; OUTPUT
;	CY = 0	SUCCESSFUL TRANSFER
;	CY = 1	FAILURE -- TIME OUT IN WAITING FOR STATUS
;	NEC_STATUS AREA HAS STATUS BYTE LOADED INTO IT
;	(AH) DESTROYED
;-----------------------------------------------
RESULTS PROC	NEAR
	CLD
	MOV	DI,OFFSET NEC_STATUS	; POINTER TO DATA AREA
	PUSH	CX			; SAVE COUNTER
	PUSH	DX
	PUSH	BX
	MOV	BL,7			; MAX STATUS BYTES

;------ WAIT FOR REQUEST FOR MASTER
R10:	MOV	BH,2			; HIGH ORDER COUNTER
J38:					; INPUT_LOOP
	XOR	CX,CX			; COUNTER
	MOV	DX,03F4H		; STATUS PORT
J39:					; WAIT FOR MASTER
	IN	AL,DX			; GET STATUS
	TEST	AL,080H 		; MASTER READY
	JNZ	J40A			; TEST_DIR
	LOOP	J39			; WAIT_MASTER
;
	DEC	BH			; DECREMENT HIGH ORDER COUNTER
	JNZ	J38			; REPEAT TIL DELAY DONE
;
	OR	DISKETTE_STATUS,TIME_OUT
J40:					; RESULTS_ERROR
	STC				; SET ERROR RETURN
	POP	BX
	POP	DX
	POP	CX
	RET

;------ TEST THE DIRECTION BIT

J40A:
	IN	AL,DX		; GET STATUS REG AGAIN
	TEST	AL,040H 	; TEST DIRECTION BIT
	JNZ	J42		; OK TO READ STATUS
J41:				; NEC_FAIL
	OR	DISKETTE_STATUS,BAD_NEC
	JMP	J40		; RESULTS ERROR

;------ READ IN THE STATUS

J42:				; INPUT_STAT
	INC	DX		; POINT AT DATA PORT
	IN	AL,DX			; GET THE DATA
	MOV	[DI],AL 		; STORE THE BYTE
	INC	DI			; INCREMENT THE POINTER
	MOV	CX,20			; LOOP TO KILL TIME FOR NEC
J43:	LOOP	J43
	DEC	DX			; POINT AT STATUS PORT
	IN	AL,DX			; GET STATUS
	TEST	AL,010H 		; TEST FOR NEC STILL BUSY
	JZ	J44			; RESULTS DONE
	DEC	BL			; DECREMENT THE STATUS COUNTER
	JNZ	R10		       ; GO BACK FOR MORE
	JMP	J41			; CHIP HAS FAILED

;------ RESULT OPERATION IS DONE

J44:
	POP	BX
	POP	DX
	POP	CX			; RECOVER REGISTERS
	RET				; GOOD RETURN CODE FROM TEST INST
;-------------------------------------------------
; NUM_TRANS
;	THIS ROUTINE CALCULATES THE NUMBER OF SECTORS THAT
;	WERE ACTUALLY TRANSFERRED TO/FROM THE DISKETTE
; INPUT
;	(CH) = CYLINDER OF OPERATION
;	(CL) = START SECTOR OF OPERATION
; OUTPUT
;	(AL) = NUMBER ACTUALLY TRANSFERRED
;	NO OTHER REGISTERS MODIFIED
;---------------------------------------------------------------
NUM_TRANS	PROC	NEAR
	MOV	AL,NEC_STATUS+3 	; GET CYLINDER ENDED UP ON
	CMP	AL,CH			; SAME AS WE STARTED
	MOV	AL,NEC_STATUS+5 	; GET ENDING SECTOR
	JZ	J45			; IF ON SAME CYL, THEN NO ADJUST
	MOV	BX,8
	CALL	GET_PARM		; GET EOT VALUE
	MOV	AL,AH			;  INTO AL
	INC	AL			; USE EOT+1 FOR CALCULATION
J45:	SUB	AL,CL			; SUBTRACT START FROM END
	RET
NUM_TRANS	ENDP
RESULTS 	ENDP

;-------------------------------------------------
;
;	HANDLE DISK CHANGE IF FOUND TO BE
;	ACTIVE
;
;-------------------------------------------------
J1F:	MOV	DSK_STATE[BX],POA_DUAL	; CLEAR STATE FOR THIS DRIVE
;
;	THIS SEQUENCE OF SEEKS IS USED TO RESET DISKETTE CHANGE SIGNAL
;
	CALL	DISK_RESET	; RESET NEC
	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	MOV	CH,01H		; MOVE TO CYLINDER 1
	CALL	SEEK		; ISSUE SEEK
	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	MOV	CH,00H		; MOVE TO CYLINDER 0
	CALL	SEEK		; ISSUE SEEK
	MOV	DISKETTE_STATUS,MEDIA_CHANGE ; INDICATE MEDIA REMOVED FROM DRIVE
	POP	DX		; RESTORE PARAMETERS
	POP	CX		; *
	POP	BX		; *
	POP	AX		; *
	RET			; MEDIA CHANGE, GO DETERMINE NEW TYPE
;---------------------------------------------------------
; READ_DSKCHNG
;  THIS ROUTINE READS THE STATE OF THE
;  DISK CHANGE LINE
;    ZERO FLAG:
;	0 - DISK CHANGE LINE INACTIVE
;	1 - DISK CHANGE LINE ACTIVE
;---------------------------------------------------------
READ_DSKCHNG	PROC	NEAR
	XOR	BH,BH		; CLEAR HIGH ORDER OFFSET
	MOV	BL,DL		; LOAD DRIVE NUMBER AS OFFSET
	MOV	AL,01		; MASK FOR DETERMINING MOTOR BIT
	AND	MOTOR_STATUS,0CFH ; CLEAR ENCODED DRIVE SELECT BITS(4 & 5)
	MOV	CL,4		; SHIFT DRIVE NUMBER INTO HIGH NIBBLE COUNT
	ROL	BL,CL		; SHIFT DRIVE NUMBER INTO HIGH NIBBLE
	OR	MOTOR_STATUS,BL ; ADD IN DRIVE NUMBER SELECTED FOR LATER USE
	ROR	BL,CL		; RESTORE DRIVE NUMBER
	MOV	CL,BL		; RESTORE DRIVE NUMBER
	SHL	AL,CL		; FORM MOTOR ON BIT MASK
	CLI			; NO INTERRUPTS WHILE DETERMING MOTOR STATUS
	TEST	AL,MOTOR_STATUS ; TEST
	JNZ	R8		; DONT NEED TO SELECT DEVICE IF MOTOR ON
;
	OR	MOTOR_STATUS,AL ; TURN ON CURRENT MOTOR
	MOV	MOTOR_COUNT,0FFH ; SET LARGE COUNT DURING OPERATION
R8:	STI			; ENABLE INTERRUPTS AGAIN
	MOV	DX,03F2H	; ADDRESS DIGITAL OUTPUT REGISTER
	MOV	AL,MOTOR_STATUS ; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,03FH 	; STRIP AWAY UNWANTED BITS
	MOV	CL,4		; SHIFT COUNT
	ROL	AL,CL		; PUT BITS IN DESIRED POSITIONS
	OR	AL,0CH		; NO RESET, ENABLE DMA/INT
	OUT	DX,AL		; SELECT DRIVE
	MOV	DX,03F7H	; ADDRESS DIGITAL INPUT REGISTER
	JMP	$+2		; DELAY FOR SUPPORT CHIP
	IN	AL,DX		; INPUT DIR
	TEST	AL,DSK_CHG	; CHECK FOR DISK CHANGE LINE ACTIVE
	RET			; RETURN TO CALLER WITH ZERO FLAG SET
READ_DSKCHNG	ENDP
;-------------------------------------------------
; DISK_CHANGE
;  THIS ROUTINE RETURNS THE STATE OF THE
;  DISK CHANGE LINE.
;    DISKETTE_STATUS:
;	00 - DISK CHANGE LINE INACTIVE
;	06 - DISK CHANGE LINE ACTIVE
;-------------------------------------------------
DISK_CHANGE	PROC	NEAR
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	DC2		; DISKETTE ATTACH CARD, SET CHANGE LINE ACTIVE
;
	XOR	BH,BH		; CLEAR HIGH ORDER OFFSET
	MOV	BL,DL		; LOAD DRIVE NUMBER AS OFFSET
	MOV	AL,DSK_STATE[BX] ; GET MEDIA STATE INFORMATION FOR DRIVE
	AND	AL,STATE_MSK	; ISOLATE STATE
	CMP	AL,3		; CHECK FOR 48TPI DRIVE & NOT ESTABLISHED STATES
	JE	SETIT		; IF FOUND SET DISK CHANGE ACTIVE
;
	JB	DC0		; IF NOT ESTABLISHED, GO CHECK FOR NO DRIVE
;
	CALL	READ_DSKCHNG	; GO CHECK STATE OF DISK CHANGE LINE
	JZ	FINIS		; CHANGE LINE NOT ACTIVE, RETURN
;
SETIT:	MOV	DISKETTE_STATUS,MEDIA_CHANGE ; INDICATE MEDIA REMOVED FROM DRIVE
FINIS:	RET			; RETURN TO CALLER
;
DC0:	MOV	AL,DSK_STATE[BX] ; GET MEDIA STATE INFORMATION FOR DRIVE
	OR	AL,AL		; CHECK FOR NO DRIVE INSTALLED
	JNZ	SETIT		; IF DRIVE PRESENT, SET CHANGE LINE ACTIVE
;
DC1:	OR	DISKETTE_STATUS,TIME_OUT ; SET TIMEOUT, BECAUSE NO DRIVE PRESENT
	RET			; RETURN TO CALLER
;
DC2:	MOV	AL,CMOSDSB_ADDR ; GET CMOS DIAGNOSTIC STATUS BYTE ADDRESS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET CMOS STATUS
	TEST	AL,CMOS_GOOD	; SEE IF BATTERY GOOD AND CHECKSUM VALID
	JNZ	DC1		; ERROR IF EITHER BIT ON
;
	MOV	AL,CMOSDSK_BYTE ; ADDRESS OF DSKETTE BYTE IN CMOS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET DSKETTE BYTE
	OR	DL,DL		; SEE WHICH DRIVE IN QUESTION
	JNZ	DC3		; IF DRIVE 1, DATA ALREADY IN LOW NIBBLE
;
	MOV	CL,4		; GET ROTATE COUNT TO SHIFT HIGH TO LOW NIBBLE
	ROR	AL,CL		; EXCHANGE NIBBLES
DC3:	AND	AL,LOWNIB	; CLEAR AWAY UNDESIRED DRIVE DATA
	JZ	DC1		; NO DRIVE THEN SET TIMEOUT ERROR
;
	JMP	SHORT SETIT	; DRIVE, ON 320/360K DRIVES SET DISK CHANGE
DISK_CHANGE ENDP
;-------------------------------------------------
; DISK_TYPE
;  THIS ROUTINE IS USED TO EITHER ESTABLISH THE
;  TYPE OF MEDIA/DRIVE TO BE USED IN THE NEXT
;  OPERATION(FOR FORMAT ONLY) OR RETURN THE
;  TYPE OF MEDIA/DRIVE INSTALLED AT THE DRIVE
;  SPECIFIED
;-------------------------------------------------
DISK_TYPE	PROC	NEAR
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	T2		; DISKETTE ATTACH CARD, GO DO TYPE OPERATION
;
	XOR	BH,BH		; CLEAR HIGH ORDER OFFSET
	MOV	BL,DL		; LOAD DRIVE NUMBER AS OFFSET
	MOV	AH,DSK_STATE[BX] ; GET PRESENT STATE INFORMATION
;
	TEST	AH,DETERMINED	; SEE IF MEDIA/DRIVE TYPE ALREADY ESTABLISHED
	JZ	T5		; IF NOT, GO RETURN ZERO VALUE
;
	AND	AH,STATE_MSK	; STRIP OFF HIGH ORDER BITS
	SUB	AH,03H		; CONVERT TO TYPE FOR OUTPUT
	JNZ	T7		; SKIP IF NOT 320/360 DRIVE AND MEDIA
;
	MOV	AL,NOCHGLN	; INDICATE NO CHANGE LINE AVAILABLE
	RET			; RETURN TO CALLER
;
T5:	OR	AH,AH		; CHECK FOR NO DRIVE
	JZ	T1		; IF NONE GO INDICATE SUCH TO CALLER
;
	AND	AH,STATE_MSK	; STRIP OFF HIGH ORDER BITS
	JZ	TA		; IF STATE 0 CHECK CMOS
;
T7:	MOV	AL,CHGLN	; 1.2 DRIVE
	RET			; RETURN TO CALLER
;
TA:	MOV	AL,CMOSDSB_ADDR ; GET CMOS DIAGNOSTIC STATUS BYTE ADDRESS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET CMOS STATUS
	TEST	AL,CMOS_GOOD	; SEE IF BATTERY GOOD AND CHECKSUM VALID
	JNZ	T1		; ERROR IF EITHER BIT ON
;
	MOV	AL,CMOSDSK_BYTE ; ADDRESS OF DSKETTE BYTE IN CMOS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET DSKETTE BYTE
	OR	DL,DL		; SEE WHICH DRIVE IN QUESTION
	JNZ	TB		; IF DRIVE 1, DATA ALREADY IN LOW NIBBLE
;
	MOV	CL,4		; GET ROTATE COUNT TO SHIFT HIGH TO LOW NIBBLE
	ROR	AL,CL		; EXCHANGE NIBBLES
TB:	AND	AL,LOWNIB	; CLEAR AWAY UNDESIRED DRIVE DATA
	CMP	AL,3		; SEE IF UNDEFINED DISKETTE TYPE
	JB	TC		; RETURN IF NOT, RESULTS IN AL
;
T1:	XOR	AL,AL		; STATE NO DRIVE PRESENT OR UNKNOWN
TC:	RET			; RETURN TO CALLER
;
T2:	MOV	AL,CMOSDSB_ADDR ; GET CMOS DIAGNOSTIC STATUS BYTE ADDRESS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET CMOS STATUS
	TEST	AL,CMOS_GOOD	; SEE IF BATTERY GOOD AND CHECKSUM VALID
	JNZ	T1		; ERROR IF EITHER BIT ON
;
	MOV	AL,CMOSDSK_BYTE ; ADDRESS OF DSKETTE BYTE IN CMOS
	OUT	CADR_PRT,AL	; WRITE ADDRESS TO READ OUT TO CMOS
	JMP	$+2		; DELAY
	IN	AL,CDATA_PRT	; GET DSKETTE BYTE
	OR	DL,DL		; SEE WHICH DRIVE IN QUESTION
	JNZ	T3		; IF DRIVE 1, DATA ALREADY IN LOW NIBBLE
;
	MOV	CL,4		; GET ROTATE COUNT TO SHIFT HIGH TO LOW NIBBLE
	ROR	AL,CL		; EXCHANGE NIBBLES
T3:	AND	AL,LOWNIB	; CLEAR AWAY UNDESIRED DRIVE DATA
	CMP	AL,INVALID_DRV	; SEE IF UNDEFINED DISKETTE TYPE
	JB	T6		; RETURN IF NOT, RESULTS IN AL
;
	XOR	AL,AL		; STATE NO DRIVE PRESENT OR UNKNOWN
T6:	RET			; RETURN TO CALLER
DISK_TYPE ENDP
;-------------------------------------------------
; FORMAT_SET
;  THIS ROUTINE IS USED TO ESTABLISH THE
;  TYPE OF MEDIA/DRIVE TO BE USED FOR THE FOLLOWING
;  FORMAT OPERATION
;-------------------------------------------------
FORMAT_SET	PROC	NEAR
	TEST	HF_CNTRL,DUAL	; GO DETERMINE TYPE OF CONTROLLER CARD
	JZ	S0		; DISKETTE ATTACH CARD, GO DO TYPE OPERATION
;
	XOR	BH,BH		; CLEAR HIGH ORDER OFFSET
	MOV	BL,DL		; LOAD DRIVE NUMBER AS OFFSET
	DEC	AL		; CHECK FOR 320/360K MEDIA & DRIVE
	JNZ	S1		; BYPASS IF NOT
;
	MOV	DSK_STATE[BX],M326D326 ; SET STATE VARIABLE
	RET			; RETURN TO CALLER
;
S1:	PUSH	AX		; SAVE TYPE VALUE
	CALL	READ_DSKCHNG	; GO CHECK DISK CHANGE LINE
	JZ	S3		; NOT ACTIVE GO ON PROCESSING
;
	MOV	DISKETTE_STATUS,MEDIA_CHANGE ; INDICATE DISK CHANGE ACTIVE
	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	MOV	CH,01H		; MOVE TO CYLINDER 1
	CALL	SEEK		; ISSUE SEEK
	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	MOV	CH,00H		; MOVE TO CYLINDER 0
	CALL	SEEK		; ISSUE SEEK
	MOV	DX,[BP] 	; RESTORE DRIVE PARMETER
	CALL	READ_DSKCHNG	; GO CHECK DISK CHANGE LINE
	JZ	S3		; CHANGE LINE INACTIVE, GO SET TYPE
;
	POP	AX		; RESTORE TYPE VALUE
	MOV	DISKETTE_STATUS,TIME_OUT ; INDICATE NO MEDIA IN DRIVE
	MOV	BX,[BP] 	; RESTORE DRIVE PARMETER FOR USE AS INDEX
	XOR	BH,BH		; CLEAR HIGH ORDER OFFSET
	MOV	DSK_STATE[BX],POA_DUAL ; SET STATE TO POWER ON ASSUMPTION
	RET			; RETURN TO CALLER
;
S3:	POP	AX		; RESTORE TYPE VALUE
	DEC	AL		; CHECK FOR 320/360K MEDIA IN 1.2M DRIVE
	JNZ	S2		; BYPASS IF NOT
;
	MOV	DSK_STATE[BX],M326D12 ; SET STATE VARIABLE
	RET			; RETURN TO CALLER
;
S2:	DEC	AL		; CHECK FOR 1.2M MEDIA IN 1.2M DRIVE
	JNZ	SE		; BYPASS IF NOT, ERROR CONDITION NOW EXISTS
;
	MOV	DSK_STATE[BX],M12D12 ; SET STATE VARIABLE
	RET			; RETURN TO CALLER
;
SE:	MOV	DISKETTE_STATUS,BAD_CMD ; UNKNOWN STATE,BAD COMMAND
S0:	RET			; RETURN TO CALLER
FORMAT_SET	ENDP

;-------------------------------------------------
; DSKETTE_SETUP:
;  THIS ROUTINE DOES A PRELIMINARY CHECK TO SEE
;  WHAT TYPE OF DISKETTE DRIVES ARE ATTACH TO THE
;  SYSTEM.  TEST IS ONLY PERFORMED WHEN A DUAL
;  ATTACHMENT CARD EXISTS.
;-------------------------------------------------
DSKETTE_SETUP PROC	NEAR
	PUSH	AX		; SAVE REGISTERS
	PUSH	BX		; *
	PUSH	CX		; *
	PUSH	DX		; *
	PUSH	SI		; *
	PUSH	DI		; *
	PUSH	ES		; *
	PUSH	DS		; *
	PUSH	BP		; *
	CALL	DDS		; LOAD DATA SEGMENT REGISTER TO ROM BIOS AREA
	MOV	BX,0		; INITIALIZE DRIVE POINTER
	MOV	WORD PTR DSK_STATE[BX],0 ; INITIALIZE STATES
	MOV	WORD PTR DSK_STATE[BX+2],0 ; INITIALIZE START STATES
	MOV	LASTRATE,0	; INITIALIZE LAST DATA TRANSFER RATE
	MOV	SEEK_STATUS,0	; INDICATE RECALIBRATE NEEDED
	MOV	MOTOR_COUNT,0	; INITIALIZE MOTOR COUNT
	MOV	MOTOR_STATUS,0	; INITIALIZE DRIVES TO OFF STATE
SUP0:	PUSH	BX		; SAVE POINTER
	MOV	AL,01		; MASK FOR DETERMINING MOTOR BIT
	AND	MOTOR_STATUS,0CFH ; CLEAR ENCODED DRIVE SELECT BITS(4 & 5)
	MOV	CL,4		; SHIFT DRIVE NUMBER INTO HIGH NIBBLE COUNT
	ROL	BL,CL		; SHIFT DRIVE NUMBER INTO HIGH NIBBLE
	OR	MOTOR_STATUS,BL ; ADD IN DRIVE NUMBER SELECTED FOR LATER USE
	ROR	BL,CL		; RESTORE DRIVE NUMBER
	MOV	CL,BL		; RESTORE DRIVE NUMBER
	SHL	AL,CL		; FORM MOTOR ON BIT MASK
	CLI			; NO INTERRUPTS WHILE DETERMING MOTOR STATUS
	TEST	AL,MOTOR_STATUS ; TEST
	JNZ	SUP2		; DONT NEED TO SELECT DEVICE IF MOTOR ON
;
	OR	MOTOR_STATUS,AL ; TURN ON CURRENT MOTOR
	MOV	MOTOR_COUNT,0FFH ; SET LARGE COUNT DURING OPERATION
SUP2:	STI			; ENABLE INTERRUPTS AGAIN
	MOV	DX,03F2H	; ADDRESS DIGITAL OUTPUT REGISTER
	MOV	AL,MOTOR_STATUS ; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,03FH 	; STRIP AWAY UNWANTED BITS
	MOV	CL,4		; SHIFT COUNT
	ROL	AL,CL		; PUT BITS IN DESIRED POSITIONS
	OR	AL,0CH		; NO RESET, ENABLE DMA/INT
	OUT	DX,AL		; SELECT DRIVE
	MOV	DX,BX		; ESTABLISH DRIVE PARM FOR SEEK ROUTINE
	MOV	CH,TRK_SLAP	; GET TRACK TO SEEK TO(>40)
	CALL	SEEK		; SEEK TO TRACK
	POP	DX		; RESTORE POINTER
	PUSH	DX		; SAVE POINTER
	MOV	CH,QUIET_SEEK	; SEEK SO FAR IN, BEFORE ISSUING SINGLE STEPS
	CALL	SEEK		; SEEK TO TRACK 10
	MOV	CH,QUIET_SEEK	; GET TRACK AT PRESENTLY
	XOR	SI,SI		; CLEAR SEEK COUNTER
SUP3:	DEC	CH		; SEEK TO NEXT TRACK, TOWARDS TRACK 0
	POP	DX		; RESTORE POINTER
	PUSH	DX		; SAVE POINTER
	PUSH	SI		; SAVE COUNTER
	CALL	SEEK		; SEEK TO TRACK
;
	MOV	AH,SENSE_DRV_ST ; SENSE DRIVE STATUS COMMAND BYTE
	CALL	SUP5		; ISSUE THE COMMAND
	CALL	RESULTS 	; GO GET STATUS
	POP	SI		; RESTORE COUNTER
	INC	SI		; COUNT NUMBER OF SEEKS TIL AT HOME(TRACK 0)
;
	TEST	NEC_STATUS,HOME ; LOOK TO SEE IF HEAD IS AT TRACK 0
	JNZ	SUP4		; GO DETERMINE DRIVE TYPE
;
	CMP	SI,QUIET_SEEK+1 ; SEE IF THE NUMBER OF SEEKS = NUMBER ISSUED
	JB	SUP3		; IF LESS THAN, NOT DONE YET
;
	POP	BX		; RESTORE POINTER
	JMP	SHORT NXT_DRV	; DRIVE NOT INSTALL, BYPASS
;
SUP4:	POP	BX		; RESTORE POINTER
	CMP	SI,QUIET_SEEK	; SEE IF SEEKS STEPPED EQUAL THE ORIGINAL
	MOV	DSK_STATE[BX],POA_DUAL ; SETUP POWER ON ASSUMPTION
	JAE	NXT_DRV 	; IF YES 1.2 DRIVE
;
	MOV	DSK_STATE[BX],M326D326 ; ESTABLISH 320/360K STATE
NXT_DRV:
	INC	BX		; POINT TO NEXT DRIVE
	CMP	BX,MAX_DRV	; SEE IF DONE
	JE	SUP1		; IF FINISHED LEAVE TEST
;
	JMP	SUP0		; REPEAT TIL DONE FOR EACH DRIVE
;
SUP1:	POP	BP		; RESTORE ALL REGISTERS
	POP	DS		; *
	POP	ES		; *
	POP	DI		; *
	POP	SI		; *
	POP	DX		; *
	POP	CX		; *
	POP	BX		; *
	POP	AX		; *
	RET			; OTHERWISE RETURN

;------- KEEP STACK CORRECT FOR CALL TO NEC_OUTPUT IF ERROR

SUP5:	CALL	NEC_OUTPUT	; OUTPUT TO NEC
	MOV	AH,DL		; GET DRIVE NUMBER SELECTED
	CALL	NEC_OUTPUT	; OUTPUT TO NEC
	RET			;

DSKETTE_SETUP	ENDP

CODE	ENDS
	END
